package com.dmagic.cms.common.dao;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;
import javax.persistence.Table;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.SQLQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;

import com.dmagic.cms.common.entity.ModifyInfoEntity;
import com.dmagic.cms.common.exception.ValidateException;
import com.dmagic.cms.common.util.DaoUtil;
import com.dmagic.cms.common.util.NumberUtil;
import com.dmagic.cms.common.util.SpringSecurityUtils;
import com.dmagic.cms.common.util.StringUtil;
import com.dmagic.cms.common.util.reflection.BeanParser;

/**
 * [ticket id] Description of the class
 * 
 * @author songlin.li @ Version : V<Ver.No> <Oct 26, 2012>
 */
public class CommonDao<T extends ModifyInfoEntity, PK extends Serializable> extends
		HibernateDao<T, Serializable> {

	@Resource
	private JdbcTemplate jdbcTemplate;

	@Autowired
	private DaoUtil daoUtil;

	/**
	 * update by sql, jdbcTemplate mode
	 * 
	 * @param sql
	 * @param objs
	 */
	public void saveOrUpdate(String sql, Object... objs) {
		this.getJdbcTemplate().update(sql, objs);
	}

	/**
	 * if entityClass is null, then return map list
	 * 
	 * @author songlin.li
	 * @param hql
	 * @param valueMap
	 * @return
	 */
	public List<T> query(String sql, Map<String, Object> valueMap, Class<T> entityClass) {
		SQLQuery query = this.getSession().createSQLQuery(sql);
		if (query != null && valueMap != null && valueMap.size() > 0) {
			Set<Map.Entry<String, Object>> entrys = valueMap.entrySet();
			for (Map.Entry<String, Object> entry : entrys) {
				String key = entry.getKey();
				Object value = entry.getValue();
				if (value instanceof Object[] && value != null) {
					Object[] os = (Object[]) value;
					query.setParameterList(key, os);
				} else if (value instanceof Collection && value != null) {
					Collection<Object> os = (Collection<Object>) value;
					query.setParameterList(key, os);
				} else if (StringUtil.isNotBlank(value)) {
					query.setParameter(key, value);
				}
			}
		}
		if (entityClass != null) {
			query.addEntity(entityClass);
		} else {
			query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
		}
		return query.list();
	}

	public java.sql.Blob ObjectToBlob(Object obj) throws IOException {
		try {
			ByteArrayOutputStream saos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(saos);
			oos.writeObject(obj);
			oos.flush();
			return Hibernate.getLobCreator(getSession()).createBlob(saos.toByteArray());
		} catch (Exception e) {
			logger.error("", e.getMessage());
			return null;
		}
	}

	public static Object BlobToObject(java.sql.Blob desBlob) throws IOException, SQLException,
			ClassNotFoundException {
		ObjectInputStream in = new ObjectInputStream(desBlob.getBinaryStream());
		Object obj = in.readObject();
		in.close();
		return obj;
	}

	/**
	 * @return the jdbcTemplate
	 */
	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	/**
	 * return daoUtil
	 * 
	 * @author songlin.li
	 * @return
	 */
	public DaoUtil getDaoUtil() {
		return daoUtil;
	}

	/**
	 * rewrite for service partner business
	 * 
	 * @see com.crown.common.dao.SimpleHibernateDao#save(java.lang.Object)
	 */
	public void saveEntity(T entity) throws ValidateException {
		entity.setModifiedBy(SpringSecurityUtils.getCurrentUserName());
		entity.setModifiedOn(new Date());
		// entity.validate();
		// super.evict(entity);
		super.save(entity);
		// super.merge(entity);
		super.flush();
		// super.clear();
	}

	/**
	 * getEntityById
	 * 
	 * @author Cy.zhong
	 * @param id
	 * @return T class
	 */
	public T getEntityById(final Serializable id) {
		return get(id);
	}

	/**
	 * logical delete, update set isObsoleteDate = system date rewrite delete
	 * entity for service partner business
	 * 
	 * @author songlin.li
	 * @param entity
	 */
	public void deleteEntity(final T entity) {
		entity.setModifiedBy(SpringSecurityUtils.getCurrentUserName());
		entity.setModifiedOn(new Date());
		String entityName = entityClass.getName().toLowerCase();
		String[] entityNames = entityName.split("\\.");
		entityName = entityNames[entityNames.length - 1];
		logger.debug("get entity id:" + entityName + "." + getIdName());
		Object id = BeanParser.doParse(entity, entityName + "." + getIdName());
		if (id == null) {
			deleteEntityById(null);
		} else if (id instanceof Long) {
			deleteEntityById(NumberUtil.toLong(id));
		} else if (id instanceof Integer) {
			deleteEntityById(NumberUtil.toInteger(id));
		}
	}

	/**
	 * logical delete, update set isObsoleteDate = system date rewrite delete
	 * entity for service partner business
	 * 
	 * @author songlin.li
	 * @param id
	 */
	public void deleteEntityById(final Serializable id) {
		Assert.notNull(id, "id Can not be null");
		String hql = "update " + entityClass.getSimpleName() + " set isObsoleteDate = ? where " + getIdName()
				+ "= ?";
		createQuery(hql, new Date(), id).executeUpdate();
		logger.debug("delete entity {},id is {}", entityClass.getSimpleName(), id);
	}

	/**
	 * getEntityByIdForLog
	 * 
	 * @author Cy.zhong
	 * @param id
	 * @return
	 */
	public T getEvictEntityById(final Serializable id) {
		T t = getEntityById(id);
		this.evict(t);
		return t;
	}

	/**
	 * physically deleted
	 * 
	 * @author songlin.li
	 * @param id
	 */
	public void deleteById(final Serializable id) {
		Assert.notNull(id, "id Can not be null");
		logger.debug("delete entity {}, id is {}", "", id);
		super.delete(id);
	}

	/**
	 * physically deleted
	 * 
	 * @author songlin.li
	 * @param entity
	 */
	public void deleteByEntity(final T entity) {
		Assert.notNull(entity, "entity Can not be null");
		logger.debug("delete entity {}", entity);
		super.delete(entity);
	}

	/**
	 * get an entity include physically deleted and logical delete
	 * 
	 * @author songlin.li
	 * @return
	 */
	public T getById(final Serializable id) {
		Table table = entityClass.getAnnotation(Table.class);
		String schema = "";
		if (table != null && StringUtil.isNotBlank(table.schema()))
			schema = table.schema() + ".";
		String sql = "select * from " + schema + entityClass.getSimpleName() + " where " + getIdName()
				+ "= ?";
		return queryUniqueBySQL(sql, id);
	}
}
